import serial
import cv2
import time
import threading
import numpy as np
from PIL import ImageGrab

kernel = np.ones((5, 5), np.uint8)


class SerialPort:
    message = ''

    def __init__(self, port, buand):
        super(SerialPort, self).__init__()
        self.port = serial.Serial(port, buand)
        self.port.close()
        if not self.port.isOpen():
            self.port.open()

    def port_open(self):
        if not self.port.isOpen():
            self.port.open()

    def port_close(self):
        self.port.close()

    def send_data(self):
        data = input("请输入要发送的数据（非中文）并同时接收数据: ")
        n = self.port.write((data + '\r\n').encode())
        return n

    def read_data(self):
        while True:
            self.message = self.port.readline()
            print(self.message)


###############################################################################################################
# 将多张图片聚合到一个窗口
def stackImages(scale, imgArray):
    rows = len(imgArray)
    cols = len(imgArray[0])
    rowsAvailable = isinstance(imgArray[0], list)
    width = imgArray[0][0].shape[1]
    height = imgArray[0][0].shape[0]
    if rowsAvailable:
        for x in range(0, rows):
            for y in range(0, cols):
                if imgArray[x][y].shape[:2] == imgArray[0][0].shape[:2]:
                    imgArray[x][y] = cv2.resize(imgArray[x][y], (0, 0), None, scale, scale)
                else:
                    imgArray[x][y] = cv2.resize(imgArray[x][y], (imgArray[0][0].shape[1], imgArray[0][0].shape[0]),
                                                None, scale, scale)
                if len(imgArray[x][y].shape) == 2: imgArray[x][y] = cv2.cvtColor(imgArray[x][y], cv2.COLOR_GRAY2BGR)
        imageBlank = np.zeros((height, width, 3), np.uint8)
        hor = [imageBlank] * rows
        hor_con = [imageBlank] * rows
        for x in range(0, rows):
            hor[x] = np.hstack(imgArray[x])
        ver = np.vstack(hor)
    else:
        for x in range(0, rows):
            if imgArray[x].shape[:2] == imgArray[0].shape[:2]:
                imgArray[x] = cv2.resize(imgArray[x], (0, 0), None, scale, scale)
            else:
                imgArray[x] = cv2.resize(imgArray[x], (imgArray[0].shape[1], imgArray[0].shape[0]), None, scale, scale)
            if len(imgArray[x].shape) == 2: imgArray[x] = cv2.cvtColor(imgArray[x], cv2.COLOR_GRAY2BGR)
        hor = np.hstack(imgArray)
        ver = hor
    return ver


# 将原始图片处理为可发送的黑白图片
def Img2Stack(img):  # 输入图片为原图
    imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 灰度图 边缘处理对黑白效果好
    imgBlur = cv2.GaussianBlur(imgGray, (7, 7), 1.2)  # 模糊 去除噪声
    imgCanny = cv2.Canny(imgGray, 50, 50)  # 边沿
    imgDialation = cv2.dilate(imgCanny, kernel, iterations=1)  # 迭代次数 加粗线条
    ret, thresh = cv2.threshold(imgCanny, 127, 255, cv2.THRESH_BINARY)  # 二值化单色图

    imgStack = stackImages(3, ([img, imgGray],  # 打开一个窗口一次性显示6张图片 方便对比与预览 第一个数字为单张图片缩放倍数
                               [imgBlur, imgCanny],
                               [imgDialation, thresh]))
    cv2.imshow("ImageStack", imgStack)
    return thresh  # 按照自己的需求返回对应的图片 不需要的图片可以注释掉以增加帧率


# 将可发送的黑白图片变为数组发送出去
def SendScreem(img):
    screen = np.zeros((8, 128), dtype=np.int8)  # 存储要发送出串口的数据
    for y in range(8):
        for x in range(128):
            temp = 0
            for bit in range(8):
                if (imgResult[y * 8 + bit][x] == 255):
                    temp |= 0x01 << bit
            screen[y][x] = temp
    mSerial.port.write(screen)


####################################################################################
# 测试视频全屏播放 https://www.bilibili.com/video/BV1Wb41177hQ
# cap = cv2.VideoCapture("../Resources/test_video.mp4")  # 打开一个视频
mSerial = SerialPort("COM19", 115200)  # 初始化串口
t1 = threading.Thread(target=mSerial.read_data)  # 打开串口数据读取的多线程

if __name__ == '__main__':
    t1.start()
    while True:
        beg = time.time()
        img = ImageGrab.grab()  # 获取屏幕截屏
        img = np.array(img)  # 将屏幕的截图转化为opencv可识别的np数组
        img = cv2.resize(img, (128, 64))  # 调整图片到OLED屏幕大小
        imgResult = Img2Stack(img)  # 转换处理图片
        SendScreem(imgResult)  # 将图片发送到单片机
        cv2.waitKey(1)  # 延时1ms 否则预览可能无法显示
        end = time.time()
        print('FPS:', end - beg)

